Package org.python.pydev.runners

Source Code of org.python.pydev.runners.SimpleRunner

/**
* Copyright (c) 2005-2011 by Appcelerator, Inc. All Rights Reserved.
* Licensed under the terms of the Eclipse Public License (EPL).
* Please see the license.txt included with this distribution for details.
* Any modifications to this file must keep this entire header intact.
*/
/*
* Created on 05/08/2005
*/
package org.python.pydev.runners;

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.StringTokenizer;

import org.eclipse.core.resources.IProject;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.NullProgressMonitor;
import org.eclipse.debug.core.DebugPlugin;
import org.eclipse.debug.core.ILaunchManager;
import org.python.pydev.core.IInterpreterInfo;
import org.python.pydev.core.IInterpreterManager;
import org.python.pydev.core.IPythonNature;
import org.python.pydev.core.IPythonPathNature;
import org.python.pydev.core.MisconfigurationException;
import org.python.pydev.core.docutils.StringSubstitution;
import org.python.pydev.core.docutils.StringUtils;
import org.python.pydev.core.log.Log;
import org.python.pydev.plugin.PydevPlugin;

import com.aptana.shared_core.io.FileUtils;
import com.aptana.shared_core.io.ThreadStreamReader;
import com.aptana.shared_core.string.FastStringBuffer;
import com.aptana.shared_core.structure.Tuple;
import com.aptana.shared_core.utils.PlatformUtils;

public class SimpleRunner {

    /**
     * Passes the commands directly to Runtime.exec (with the passed envp)
     */
    public static Process createProcess(String[] cmdarray, String[] envp, File workingDir) throws IOException {
        return Runtime.getRuntime().exec(getWithoutEmptyParams(cmdarray), getWithoutEmptyParams(envp), workingDir);
    }

    /**
     * @return a new array without any null/empty elements originally contained in the array.
     */
    private static String[] getWithoutEmptyParams(String[] cmdarray) {
        if (cmdarray == null) {
            return null;
        }
        ArrayList<String> list = new ArrayList<String>();
        for (String string : cmdarray) {
            if (string != null && string.length() > 0) {
                list.add(string);
            }
        }
        return list.toArray(new String[list.size()]);
    }

    /**
     * THIS CODE IS COPIED FROM org.eclipse.debug.internal.core.LaunchManager
     *
     * changed so that we always set the PYTHONPATH in the environment
     *
     * @return the system environment with the PYTHONPATH env variable added for a given project (if it is null, return it with the
     * default PYTHONPATH added).
     */
    public static String[] getEnvironment(IPythonNature pythonNature, IInterpreterInfo interpreter,
            IInterpreterManager manager) throws CoreException {
        String[] env;

        String pythonPathEnvStr = "";
        try {

            if (interpreter != null) { //check if we have a default interpreter.
                pythonPathEnvStr = makePythonPathEnvString(pythonNature, interpreter, manager);
            }
            env = createEnvWithPythonpath(pythonPathEnvStr, pythonNature, manager);

        } catch (Exception e) {
            Log.log(e);
            //We cannot get it. Log it and keep with the default.
            env = getDefaultSystemEnvAsArray(pythonNature);
        }

        if (interpreter != null) {
            env = interpreter.updateEnv(env);
        }

        return env;
    }

    /**
     * Same as the getEnvironment, but with a pre-specified pythonpath.
     * @throws MisconfigurationException
     */
    public static String[] createEnvWithPythonpath(String pythonPathEnvStr, String interpreter,
            IInterpreterManager manager, IPythonNature nature) throws CoreException, MisconfigurationException {
        String[] env = createEnvWithPythonpath(pythonPathEnvStr, nature, manager);
        IInterpreterInfo info = manager.getInterpreterInfo(interpreter, new NullProgressMonitor());
        env = info.updateEnv(env);
        return env;
    }

    private static String[] createEnvWithPythonpath(String pythonPathEnvStr, IPythonNature nature,
            IInterpreterManager manager) throws CoreException {
        DebugPlugin defaultPlugin = DebugPlugin.getDefault();
        if (defaultPlugin != null) {
            Map<String, String> env = getDefaultSystemEnv(defaultPlugin, nature); //no need to remove as it'll be updated       

            env.put("PYTHONPATH", pythonPathEnvStr); //put the environment
            switch (manager.getInterpreterType()) {

                case IPythonNature.INTERPRETER_TYPE_JYTHON:
                    env.put("CLASSPATH", pythonPathEnvStr); //put the environment
                    env.put("JYTHONPATH", pythonPathEnvStr); //put the environment
                    break;

                case IPythonNature.INTERPRETER_TYPE_IRONPYTHON:
                    env.put("IRONPYTHONPATH", pythonPathEnvStr); //put the environment

                    break;
            }
            return getMapEnvAsArray(env);
        } else {
            //should only happen in tests.
            return null;
        }
    }

    /**
     * @return an array with the env variables for the system with the format xx=yy 
     */
    public static String[] getDefaultSystemEnvAsArray(IPythonNature nature) throws CoreException {
        Map<String, String> defaultSystemEnv = getDefaultSystemEnv(nature);
        if (defaultSystemEnv != null) {
            return getMapEnvAsArray(defaultSystemEnv);
        }
        return null;
    }

    /**
     * @return a map with the env variables for the system 
     */
    public static Map<String, String> getDefaultSystemEnv(IPythonNature nature) throws CoreException {
        DebugPlugin defaultPlugin = DebugPlugin.getDefault();
        return getDefaultSystemEnv(defaultPlugin, nature);
    }

    /**
     * @return a map with the env variables for the system 
     */
    @SuppressWarnings("unchecked")
    private static Map<String, String> getDefaultSystemEnv(DebugPlugin defaultPlugin, IPythonNature nature)
            throws CoreException {
        if (defaultPlugin != null) {
            ILaunchManager launchManager = defaultPlugin.getLaunchManager();

            // build base environment
            Map<String, String> env = new HashMap<String, String>();
            env.putAll(launchManager.getNativeEnvironment());

            // Add variables from config
            boolean win32 = PlatformUtils.isWindowsPlatform();
            for (Iterator iter = env.entrySet().iterator(); iter.hasNext();) {
                Map.Entry entry = (Map.Entry) iter.next();
                String key = (String) entry.getKey();
                if (win32) {
                    // Win32 vars are case insensitive. Uppercase everything so
                    // that (for example) "pAtH" will correctly replace "PATH"
                    key = key.toUpperCase();
                }
                String value = (String) entry.getValue();
                // translate any string substitution variables
                String translated = value;
                try {
                    StringSubstitution stringSubstitution = new StringSubstitution(nature);
                    translated = stringSubstitution.performStringSubstitution(value, false);
                } catch (Exception e) {
                    Log.log(e);
                }
                env.put(key, translated);
            }

            //Always remove PYTHONHOME from the default system env, as it doesn't work well with multiple interpreters.
            env.remove("PYTHONHOME");
            return env;
        }
        return null;
    }

    /**
     * copied from org.eclipse.jdt.internal.launching.StandardVMRunner
     * @param args - other arguments to be added to the command line (may be null)
     * @return
     */
    public static String getArgumentsAsStr(String[] commandLine, String... args) {
        if (args != null && args.length > 0) {
            String[] newCommandLine = new String[commandLine.length + args.length];
            System.arraycopy(commandLine, 0, newCommandLine, 0, commandLine.length);
            System.arraycopy(args, 0, newCommandLine, commandLine.length, args.length);
            commandLine = newCommandLine;
        }

        if (commandLine.length < 1)
            return ""; //$NON-NLS-1$
        FastStringBuffer buf = new FastStringBuffer();
        FastStringBuffer command = new FastStringBuffer();
        for (int i = 0; i < commandLine.length; i++) {
            if (commandLine[i] == null) {
                continue; //ignore nulls (changed from original code)
            }

            buf.append(' ');
            char[] characters = commandLine[i].toCharArray();
            command.clear();
            boolean containsSpace = false;
            for (int j = 0; j < characters.length; j++) {
                char character = characters[j];
                if (character == '\"') {
                    command.append('\\');
                } else if (character == ' ') {
                    containsSpace = true;
                }
                command.append(character);
            }
            if (containsSpace) {
                buf.append('\"');
                buf.append(command.toString());
                buf.append('\"');
            } else {
                buf.append(command.toString());
            }
        }
        return buf.toString();
    }

    /**
     * Creates a string that can be passed as the PYTHONPATH
     *
     * @param project the project we want to get the settings from. If it is null, the system pythonpath is returned
     * @param interpreter this is the interpreter to be used to create the env.
     * @return a string that can be used as the PYTHONPATH env variable
     */
    public static String makePythonPathEnvString(IPythonNature pythonNature, IInterpreterInfo interpreter,
            IInterpreterManager manager) {
        if (pythonNature == null) {
            if (interpreter == null) {
                return makePythonPathEnvFromPaths(new ArrayList<String>()); //no pythonpath can be gotten (set to empty, so that the default is gotten)
            } else {
                List<String> pythonPath = interpreter.getPythonPath();
                return makePythonPathEnvFromPaths(pythonPath);
            }
        }

        List<String> paths;

        //if we have a project, get its complete pythonpath
        IPythonPathNature pythonPathNature = pythonNature.getPythonPathNature();
        if (pythonPathNature == null) {
            IProject project = pythonNature.getProject();
            String projectName;
            if (project == null) {
                projectName = "null?";
            } else {
                projectName = project.getName();
            }
            throw new RuntimeException("The project " + projectName + " does not have the pythonpath configured, \n"
                    + "please configure it correcly (please check the pydev getting started guide at \n"
                    + "http://pydev.org/manual_101_root.html for better information on how to do it).");
        }
        paths = pythonPathNature.getCompleteProjectPythonPath(interpreter, manager);

        return makePythonPathEnvFromPaths(paths);
    }

    /**
     * @param paths the paths to be added
     * @return a String suitable to be added to the PYTHONPATH environment variable.
     */
    public static String makePythonPathEnvFromPaths(Collection<String> inPaths) {
        ArrayList<String> paths = new ArrayList<String>(inPaths);
        try {
            //whenever we launch a file from pydev, we must add the sitecustomize to the pythonpath so that
            //the default encoding (for the console) can be set.
            //see: http://sourceforge.net/tracker/index.php?func=detail&aid=1580766&group_id=85796&atid=577329

            paths.add(0, FileUtils.getFileAbsolutePath(PydevPlugin.getScriptWithinPySrc("pydev_sitecustomize")));
        } catch (CoreException e) {
            Log.log(e);
        }

        String separator = getPythonPathSeparator();
        return com.aptana.shared_core.string.StringUtils.join(separator, paths);
    }

    /**
     * @return the separator for the pythonpath variables (system dependent)
     */
    public static String getPythonPathSeparator() {
        return System.getProperty("path.separator"); //is system dependent, and should cover for all cases...
        //        boolean win32= isWindowsPlatform();
        //        String separator = ";";
        //        if(!win32){
        //            separator = ":"; //system dependent
        //        }
        //        return separator;
    }

    /**
     * @param env a map that will have its values formatted to xx=yy, so that it can be passed in an exec
     * @return an array with the formatted map
     */
    private static String[] getMapEnvAsArray(Map<String, String> env) {
        List<String> strings = new ArrayList<String>(env.size());
        for (Iterator<Map.Entry<String, String>> iter = env.entrySet().iterator(); iter.hasNext();) {
            Map.Entry<String, String> entry = iter.next();
            StringBuffer buffer = new StringBuffer(entry.getKey());
            buffer.append('=').append(entry.getValue());
            strings.add(buffer.toString());
        }

        return strings.toArray(new String[strings.size()]);
    }

    /**
     * @return a tuple with the process created and a string representation of the cmdarray.
     */
    public Tuple<Process, String> run(String[] cmdarray, File workingDir, IPythonNature nature, IProgressMonitor monitor) {
        if (monitor == null) {
            monitor = new NullProgressMonitor();
        }
        String executionString = getArgumentsAsStr(cmdarray);
        monitor.setTaskName("Executing: " + executionString);
        monitor.worked(5);
        Process process = null;
        try {
            monitor.setTaskName("Making pythonpath environment..." + executionString);
            String[] envp = null;
            if (nature != null) {
                envp = getEnvironment(nature, nature.getProjectInterpreter(), nature.getRelatedInterpreterManager()); //Don't remove as it *should* be updated based on the nature)
            }
            //Otherwise, use default (used when configuring the interpreter for instance).
            monitor.setTaskName("Making exec..." + executionString);
            if (workingDir != null) {
                if (!workingDir.isDirectory()) {
                    throw new RuntimeException(com.aptana.shared_core.string.StringUtils.format(
                            "Working dir must be an existing directory (received: %s)", workingDir));
                }
            }
            process = createProcess(cmdarray, envp, workingDir);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
        return new Tuple<Process, String>(process, executionString);
    }

    /**
     * Runs the given command line and returns a tuple with the output (stdout and stderr) of executing it.
     *
     * @param cmdarray array with the commands to be passed to Runtime.exec
     * @param workingDir the working dir (may be null)
     * @param project the project (used to get the pythonpath and put it into the environment) -- if null, no environment is passed.
     * @param monitor the progress monitor to be used -- may be null
     *
     * @return a tuple with stdout and stderr
     */
    public Tuple<String, String> runAndGetOutput(String[] cmdarray, File workingDir, IPythonNature nature,
            IProgressMonitor monitor, String encoding) {
        Tuple<Process, String> r = run(cmdarray, workingDir, nature, monitor);

        return getProcessOutput(r.o1, r.o2, monitor, encoding);
    }

    /**
     * @param process process from where the output should be gotten
     * @param executionString string to execute (only for errors)
     * @param monitor monitor for giving progress
     * @return a tuple with the output of stdout and stderr
     */
    public static Tuple<String, String> getProcessOutput(Process process, String executionString,
            IProgressMonitor monitor, String encoding) {
        if (monitor == null) {
            monitor = new NullProgressMonitor();
        }
        if (process != null) {

            try {
                process.getOutputStream().close(); //we won't write to it...
            } catch (IOException e2) {
            }

            monitor.setTaskName("Reading output...");
            monitor.worked(5);
            //No need to synchronize as we'll waitFor() the process before getting the contents.
            ThreadStreamReader std = new ThreadStreamReader(process.getInputStream(), false, encoding);
            ThreadStreamReader err = new ThreadStreamReader(process.getErrorStream(), false, encoding);

            std.start();
            err.start();

            boolean interrupted = true;
            while (interrupted) {
                interrupted = false;
                try {
                    monitor.setTaskName("Waiting for process to finish.");
                    monitor.worked(5);
                    process.waitFor(); //wait until the process completion.
                } catch (InterruptedException e1) {
                    interrupted = true;
                }
            }

            try {
                //just to see if we get something after the process finishes (and let the other threads run).
                Object sync = new Object();
                synchronized (sync) {
                    sync.wait(50);
                }
            } catch (Exception e) {
                //ignore
            }
            return new Tuple<String, String>(std.getContents(), err.getContents());

        } else {
            try {
                throw new CoreException(PydevPlugin.makeStatus(IStatus.ERROR,
                        "Error creating process - got null process(" + executionString + ")", new Exception(
                                "Error creating process - got null process.")));
            } catch (CoreException e) {
                Log.log(IStatus.ERROR, e.getMessage(), e);
            }

        }
        return new Tuple<String, String>("", "Error creating process - got null process(" + executionString + ")"); //no output
    }

    /**
     * @param pythonpath the pythonpath string to be used
     * @return a list of strings with the elements of the pythonpath
     */
    public static List<String> splitPythonpath(String pythonpath) {
        ArrayList<String> splitted = new ArrayList<String>();
        StringTokenizer tokenizer = new StringTokenizer(pythonpath, getPythonPathSeparator());
        while (tokenizer.hasMoreTokens()) {
            splitted.add(tokenizer.nextToken());
        }
        return splitted;

    }

}
TOP

Related Classes of org.python.pydev.runners.SimpleRunner

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.